Add elasticgraph-proto_ingestion schema artifacts#1080
Conversation
2e0e4a1 to
53ea492
Compare
a3cfa5c to
9e6f39a
Compare
4325d51 to
fd8dd47
Compare
9e6f39a to
c408a50
Compare
fd8dd47 to
bdce7fb
Compare
c408a50 to
5717334
Compare
0607029 to
40815e1
Compare
6591c29 to
751db75
Compare
b2e8d34 to
6969f84
Compare
070849e to
ce625bc
Compare
e9902f1 to
f069179
Compare
5efeb79 to
7d428d5
Compare
7d428d5 to
9acfed5
Compare
9acfed5 to
4fe8938
Compare
4fe8938 to
474788d
Compare
474788d to
bcc30c1
Compare
Moves `field.renamed_from`, `type.renamed_from`, `type.deleted_field`, and `schema.deleted_type` — along with the state registries and `DeprecatedElement` record they populate — from `elasticgraph-json_ingestion` back into the core schema definition gem, partially reverting the placement chosen in #1259. These declarations record serializer-neutral facts about how a schema has evolved, and we now have a second consumer: `elasticgraph-proto_ingestion` (#1080) needs rename metadata to keep protobuf field numbers stable across renames (with `reserved` field numbers for deletions as a natural follow-up). Keeping the markers inside one serializer forced other consumers to duck-type against state that may or may not be present; with the declarations in core, every consumer reads typed `State` members directly. This also addresses the concern raised in review of #1259 about `elasticgraph-json_ingestion` bolting 9 fields onto `::ElasticGraph::SchemaDefinition::State`: its `StateExtension` now adds only the 5 genuinely JSON-specific fields, and the four deprecation registries are ordinary typed `State` members that need no `State & StateExtension` casts. All JSON-specific behavior — schema version enforcement, merge reporting, pruning, and the warnings that flag no-longer-needed declarations — stays in `elasticgraph-json_ingestion`. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
98f82ce to
683be9a
Compare
Fills in the `elasticgraph-proto_ingestion` gem with the Protocol Buffers schema artifact generation logic. Running `schema_artifacts:dump` emits: - `schema.proto` — the generated Protobuf schema for the indexed types - `proto_field_numbers.yaml` — a sidecar that reserves field numbers and enum value numbers so they stay wire-stable as the schema evolves (including across field renames, and reserving removed values so numbers are not reused) Capabilities: - Maps built-in ElasticGraph scalars to proto types, with `proto_field` to configure custom scalars. - Generates messages for object/interface/union types and enums (with a zero-valued `*_UNSPECIFIED` entry), escaping proto reserved words and wrapping nested lists so the output stays valid. - `proto_enum_mappings` reuses enum values already maintained elsewhere. The generator emits `proto3` by default and can emit `proto2` via `syntax: :proto2` (which labels every field `optional`/`repeated`). Arbitrary file-level headers (e.g. `option` declarations) can be injected verbatim via `headers:`, so language-specific options can be set without baking any particular convention into the gem.
myronmarston
left a comment
There was a problem hiding this comment.
Not done reviewing but wanted to submit what I have so far.
| # @dynamic proto_schema_syntax, proto_schema_syntax= | ||
| # @dynamic proto_schema_headers, proto_schema_headers= | ||
| attr_accessor :proto_schema_package_name, :proto_enums_by_graphql_enum, | ||
| :proto_field_number_mappings, :proto_schema_syntax, :proto_schema_headers |
There was a problem hiding this comment.
Similar to #1281 can we move these all to a separate object and then have a single proto_state field on schema_def_api.state?
| schema.scalar_type "Money" do |t| | ||
| t.mapping type: "long" | ||
| t.json_schema type: "integer" | ||
| t.proto_field type: "int64" |
There was a problem hiding this comment.
It feels odd for this to be called proto_field--we're configuring a type, not a field so why does the API say "field"? And it's not mapping_field/json_schema_field for the other options.
WDYT about calling this protobuf? e.g. t.protobuf type: "int64"?
| ElasticGraph.define_schema do |schema| | ||
| schema.proto_enum_mappings( | ||
| SalesEg::ProtoEnumMappings::PROTO_ENUMS_BY_GRAPHQL_ENUM | ||
| ) if defined?(SalesEg::ProtoEnumMappings) |
There was a problem hiding this comment.
Not sure you want to name a block-internal project here...
| ``` | ||
|
|
||
| When a mapping exists for an enum, `elasticgraph-proto_ingestion` uses the mapped proto enum(s) | ||
| as the source of enum values (respecting `exclusions`, `expected_extras`, and `name_transform`). |
There was a problem hiding this comment.
EG today doesn't have any concept of proto enum mappings, and doesn't have any logic to support exclusions, expected_extras or name_transform. Instead of importing an existing data structure designed for tests within Block internal code, can we invert this?
- Design and implement whatever features make sense for the
elasticgraph-proto_ingestiongem as a stand-alone, open source library. - Update the internal testing support inside Block to leverage what
elasticgraph-proto_ingestionprovides.
For example, instead of it being provided as a global data structure, maybe we can do something like:
schema.enum_type "Currency" do |t|
t.external_proto_enum "v1.money.Currency", # ... plus other options as needed like `exclusions`, etc.
endWith that said, I don't yet understand how this gem uses this mapping...I haven't gotten that far.
| RSpec.configure do |config| | ||
| config.when_first_matching_example_defined(:proto_schema) do | ||
| require "support/proto_schema_support" | ||
| end |
There was a problem hiding this comment.
You can also think about applying this tag automatically to specs under elasticgraph-proto_ingestion/spec, using this technique:
elasticgraph/elasticgraph-json_ingestion/spec/spec_helper.rb
Lines 16 to 18 in 4cdc065
| | `Boolean` | `bool` | | ||
| | `Cursor` | `string` | | ||
| | `Date` | `string` | | ||
| | `DateTime` | `string` | |
There was a problem hiding this comment.
Should we use https://protobuf.dev/reference/protobuf/google.protobuf/#timestamp for this?
| |-------------------|------------| | ||
| | `Boolean` | `bool` | | ||
| | `Cursor` | `string` | | ||
| | `Date` | `string` | |
There was a problem hiding this comment.
One potential concern is that string is a much wider type than Date. Something like 0.0001% of valid strings are valid ISO8601 Date strings.
Have you given thought to how invalid values will get handled? With JSON schema we could specify validations for this kind of thing.
|
|
||
| Additionally: | ||
| - List types become `repeated` fields. | ||
| - Nested list types generate wrapper messages so the output remains valid `proto3`. |
There was a problem hiding this comment.
I'm not following this.
schema.object_type "Team" do |t|
t.field "players", "[Player!]!" do |f|
f.mapping type: "nested"
end
t.field "seasons", "[Season!]!" do |f|
f.mapping type: "object"
end
endIs this saying that a wrapper message type will be generated for the players field but not the seasons field? Why would that be? And what would the proto look like?
Note
Third of a three-PR sequence: #1296 (artifact comment prefixes) and #1297 (gem scaffold) split the supporting changes out, so this PR's diff is now contained entirely to the
elasticgraph-proto_ingestiondirectory.Why
What
elasticgraph-proto_ingestionextension gem that generatesschema.protoandproto_field_numbers.yamlschema.protoon public GraphQL field names while storing privatename_in_indexoverrides in the sidecar mapping fileschema.protoartifact preamble with protobuf//comments so it remains valid proto syntaxfield.renamed_fromand update repo wiring/docs/testsenumssection ofproto_field_numbers.yaml, so removing or reordering enum values never renumbers the rest (removed values keep their numbers reserved;0remains the generated*_UNSPECIFIEDvalue)Integer()silently truncate floatstype_ref.with_reverted_overrideso built-in scalars renamed withtype_name_overrideskeep workingThis PR is stacked on #1294, which restores the schema-evolution declarations (
field.renamed_fromet al) to coreelasticgraph-schema_definition. Referencing external proto enum types (proto_external_types) is split out into the stacked follow-up PR #1286.Notes for review
elasticgraph-proto_ingestionis fully independent ofelasticgraph-json_ingestion— no runtime or development dependency. Its specs activate only the proto extension. Rename metadata comes from the corefield.renamed_fromdeclaration (see Restore schema-evolution declarations to elasticgraph-schema_definition #1294), which proto generation reads directly from typedStatemembers — norespond_to?probing. When no renames are declared, field numbers are simply assigned fresh (covered by a dedicated spec).type.deleted_fieldto emit protobufreservedfield numbers (wire-safety), andtype.renamed_fromto migrate a message's number block across type renames.Verification
script/quick_build(the one spec failure on this machine —cli_new_specunder bundler 4 — reproduces identically onmainand is environmental)script/type_check,script/lint,script/spellcheckscript/run_gem_specs elasticgraph-proto_ingestion(100% line + branch coverage)run_misc_checkssteps that CI previously never reached: config artifacts, dependency diagrams, CI yaml, licenses, README snippets,rake site:validateReferences